package com.yulan.bankback.service.bank.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.yulan.bankback.converter.UserConverter;
import com.yulan.bankback.entity.dto.bank.UserMoneyFlowGroup;
import com.yulan.bankback.entity.model.admin.IllegalTrading;
import com.yulan.bankback.entity.model.bank.*;
import com.yulan.bankback.entity.vo.CardVo;
import com.yulan.bankback.entity.vo.MoneyDetailVo;
import com.yulan.bankback.entity.vo.UserVo;
import com.yulan.bankback.log.Logger;
import com.yulan.bankback.mapper.admin.IllegalTradingMapper;
import com.yulan.bankback.mapper.admin.ViolationUserMapper;
import com.yulan.bankback.mapper.bank.*;
import com.yulan.bankback.service.bank.AuthService;
import com.yulan.bankback.service.bank.BankService;
import com.yulan.bankback.util.MD5Util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BankServiceImpl implements BankService {

    @Resource
    private BankUserMapper bankUserMapper;

    @Autowired
    private UserConverter userConverter;

    @Resource
    private BankCardMapper bankCardMapper;

    @Resource
    private CardFlowMapper cardFlowMapper;

    @Resource
    private CouponMapper couponMapper;

    @Resource
    private UserCouponMapper userCouponMapper;

    @Autowired
    private AuthService authService;

    @Resource
    private CanteenCardMapper canteenCardMapper;

    @Resource
    private HotCardMapper hotCardMapper;

    @Resource
    private NoticeMapper noticeMapper;

    @Resource
    private UserCapitalFlowMapper userCapitalFlowMapper;

    @Resource
    private ViolationUserMapper violationUserMapper;

    @Resource
    private IllegalTradingMapper illegalTradingMapper;

    @Autowired
    private Logger logger;

    private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM-dd");


    @Override
    public void addUser(BankUser bankUser) {
        bankUserMapper.insert(bankUser);
    }


    @Override
    public UserVo getUserVo(String uid) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user == null ) return null;
        UserVo userVo = new UserVo();
        userConverter.converterUser2UserVo(user, userVo);

        //查询最近的收入曲线
        List<UserMoneyFlowGroup> incomeFlows
                = userCapitalFlowMapper.getFlowByIntervalAndType("income", uid, "1 month");
        //查询最近的支出曲线
        List<UserMoneyFlowGroup> expendFlows
                = userCapitalFlowMapper.getFlowByIntervalAndType("expend", uid, "1 month");

        Map<String, Double> incomeLogMap = incomeFlows.stream().collect(
                Collectors.toMap(
                        UserMoneyFlowGroup::getTime,
                        UserMoneyFlowGroup::getMoney)
        );

        incomeLogMap.keySet().forEach(e -> e = e.split(" ")[0]);

        userVo.setInComeLogs(incomeLogMap);

        Map<String, Double> expendLogMap = expendFlows.stream().collect(
                Collectors.toMap(
                        UserMoneyFlowGroup::getTime,
                        UserMoneyFlowGroup::getMoney)
        );

        expendLogMap.keySet().forEach(e -> e = e.split(" ")[0]);

        userVo.setExpendLogs(expendLogMap);

        double totalInCome = incomeFlows.stream()
                .mapToDouble(item -> item.getMoney() == null ? 0 : item.getMoney())
                .sum();
        double totalExpand = expendFlows.stream()
                .mapToDouble(item -> item.getMoney() == null ? 0 : item.getMoney())
                .sum();

        int inComeCount = userCapitalFlowMapper.getTotal("income", uid, "1 month");

        int expendCount = userCapitalFlowMapper.getTotal("expend", uid, "1 month");

        userVo.setTotalInCome(totalInCome);
        userVo.setTotalExpend(totalExpand);
        userVo.setInComeCount(inComeCount);
        userVo.setExpendCount(expendCount);

        List<BankCard> bankCards = bankCardMapper
                .selectList(new QueryWrapper<BankCard>().eq("uid", uid));



        userVo.setBankCards(bankCards.size() > 3 ? bankCards.subList(0, 3) : bankCards);

        return userVo;
    }

    @Override
    public void recharge(String uid, Double money, String type, String cardId) {
        BankUser user = bankUserMapper.selectById(uid);
        user.setMoney(user.getMoney() + money);
        bankUserMapper.updateById(user);
        userCapitalFlowMapper.insert(new UserCapitalFlow(uid, money,
                "income", new Date(), "", new Date(), type,0));

        if(StringUtils.isNotBlank(cardId)){
            cardFlowMapper.insert(new CardFlow(cardId,"recharge", money, "",  new Date()));
        }
        logger.logRecharge(uid, String.valueOf(money));
    }

    @Override
    public List<BankCard> getUserAllCard(String uid) {
        return bankCardMapper.selectList(new QueryWrapper<BankCard>().eq("uid", uid));
    }

    @Override
    public List<CardFlow> getUserOneCardDetail(String start, String end, String type, String uid, String cid) {
        BankCard card = bankCardMapper.selectOne(
                new QueryWrapper<BankCard>()
                        .eq("uid", uid)
                        .eq("cid", cid));
        if(card == null) return null;
        QueryWrapper<CardFlow> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("cid", cid);

        if(StringUtils.isNotBlank(start)) queryWrapper.gt("create_time", start);
        if(StringUtils.isNotBlank(end)) queryWrapper.lt("create_time", end);
        if(StringUtils.isNotBlank(type)) queryWrapper.eq("type", type);
        List<CardFlow> res = cardFlowMapper.selectList(queryWrapper);
        res.forEach(item -> item.setViewDate(formatter.format(item.getCreateTime())));
        return res;
    }

    @Override
    public String withdrawal(String uid, String cid, Double money, String payPassword) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user.getMoney() < money)
            return "余额不足";
        if(!StringUtils.equals(MD5Util.encoderByMd5(payPassword), user.getPayPassword())){
            return "支付密码错误";
        }
        try{
            //扣钱
            user.setMoney(user.getMoney() - money);
            bankUserMapper.updateById(user);
            //写流水表
            userCapitalFlowMapper.insert(new UserCapitalFlow(uid, money,
                    "expend", new Date(), "",
                    new Date(), "银行卡提现", 0));
            cardFlowMapper.insert(new CardFlow(cid, "withdrawal", money, "",  new Date()));
            return "success";
        }catch (Exception ex){
            ex.printStackTrace();
            return "提现失败";
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String transfer(String srcUid, String phone, Double money, String payPassword){
        BankUser srcUser = bankUserMapper.selectById(srcUid);
        BankUser destUser = bankUserMapper.selectOne(new QueryWrapper<BankUser>().eq("phone", phone));
        if(srcUser == null )
            return "用户不存在";
        else if(destUser == null)
            return "用户不存在";
        else if(srcUser.getMoney() < money)
            return "用户余额不足";
        else if(!authService.verifyPayPassword(srcUid, payPassword))
            return "支付密码错误";

        srcUser.setMoney(srcUser.getMoney() - money);
        destUser.setMoney(destUser.getMoney() + money);

        bankUserMapper.updateById(srcUser);
        bankUserMapper.updateById(destUser);

        userCapitalFlowMapper.insert(new UserCapitalFlow(srcUid, money,
                "expend", new Date(), "", new Date(), "转账给其他用户("+ phone + ")",0));
        userCapitalFlowMapper.insert(new UserCapitalFlow(destUser.getId(), money,
                "income", new Date(), "", new Date(), "接收其他用户(" + destUser.getPhone() + ")的转账",0));


        //如果是违规用户
        if(violationUserMapper.selectById(destUser.getId()) != null){
            illegalTradingMapper.insert(new IllegalTrading(UUID.randomUUID().toString().replaceAll("-",""),
                    srcUid, srcUser.getUserName(), new Date(),"", "与违规用户发生交易",money, 0));
        }

        logger.logMoneyFlow(srcUid, destUser.getId(), String.valueOf(money), "transfer");
        return "success";
    }

    @Override
    public MoneyDetailVo moneyDetail(String uid) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user == null)
            return null;
        MoneyDetailVo moneyDetailVo = new MoneyDetailVo();
        moneyDetailVo.setFreeMoney(user.getMoney());
        moneyDetailVo.setDeadMoney(user.getNotFreeMoney());
        moneyDetailVo.setUid(uid);
        moneyDetailVo.setAccount(user.getAccount());



        List<UserBalance> balanceList7Day = bankUserMapper.getBalanceInterval(uid, "7 day");
        List<UserBalance> balanceList14Day = bankUserMapper.getBalanceInterval(uid, "14 day");
        List<UserBalance> balanceList30Day = bankUserMapper.getBalanceInterval(uid, "30 day");

        balanceList14Day = changeListSize(balanceList7Day, 4);
        balanceList30Day = changeListSize(balanceList30Day, 4);

        moneyDetailVo.setLast7DayMoneyLogs(balanceList7Day);

        moneyDetailVo.setLast14DayMoneyLogs(balanceList14Day);

        moneyDetailVo.setLast30DayMoneyLogs(balanceList30Day);

        List<UserCapitalFlow> userCapital14DayFlows
                = userCapitalFlowMapper.getFlowByInterval(uid, "14 day");
        userCapital14DayFlows = changeListSize(userCapital14DayFlows, 4);

        List<UserCapitalFlow> userCapital30DayFlows
                = userCapitalFlowMapper.getFlowByInterval(uid, "30 day");
        userCapital30DayFlows = changeListSize(userCapital30DayFlows, 4);


        userCapital14DayFlows.forEach(item ->{
            if(item.getType().equals("income")){
                item.setType("收入");
            }else if(item.getType().equals("expend")){
                item.setType("支出");
            }
            item.setViewTime(formatter.format(item.getTime()));
        });

        userCapital30DayFlows.forEach(item ->{
            if(item.getType().equals("income")){
                item.setType("收入");
            }else if(item.getType().equals("expend")){
                item.setType("支出");
            }
            item.setViewTime(formatter.format(item.getTime()));
        });


        moneyDetailVo.setUserMoney14DayDetails(userCapital14DayFlows);
        moneyDetailVo.setUserMoney30DayDetails(userCapital30DayFlows);



        return moneyDetailVo;

    }

    @Override
    public List<Coupon> getUserAllCoupon(String uid) {
        List<UserCoupon> userCoupons
                = userCouponMapper.selectList(new QueryWrapper<UserCoupon>()
                    .eq("uid", uid));


        if(userCoupons == null || userCoupons.isEmpty())
            return null;

        List<Coupon> coupons = couponMapper.selectBatchIds(
                                        userCoupons.stream()
                                        .map(UserCoupon::getCid)
                                        .collect(Collectors.toList()));

        //不能过期
        coupons = coupons.stream().filter(
                item -> item.getOverdueTime().after(new Date()))
                .collect(Collectors.toList());

        return this.formatterCouponList(coupons);
    }

    @Override
    public List<Coupon> getCouponByConditions(String start, String end, String type) {
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();

        if(StringUtils.isNotBlank(start) && StringUtils.isNotBlank(end)){
            queryWrapper.gt("start_time",start).lt("end_time",end);
        }

        if(StringUtils.isNotBlank(type))
            queryWrapper.eq("type", type);
        List<Coupon> coupons =  couponMapper.selectList(queryWrapper);

        return this.formatterCouponList(coupons);
    }

    @Override
    public List<Coupon> getAllCoupon() {
        List<Coupon> coupons = couponMapper.selectList(new QueryWrapper<Coupon>()
                .gt("end_time", formatter.format(new Date()))
                .lt("start_time", formatter.format(new Date())));

        return this.formatterCouponList(coupons);
    }

    private static HashMap<String,String> couponTypeMap = new HashMap<String,String>(){{
        put("canteen","生活优惠");
        put("book","学术优惠");
        put("hot","生活优惠");
        put("milk","商业优惠");
    }};

    @Override
    public String addCoupon(String type, String name, String start,
                            String end, String overDue, Double discount, Integer count) {
        Coupon coupon = new Coupon();
        coupon.setCouponType(type);
        coupon.setType(couponTypeMap.get(type));
        coupon.setCouponName(name);
        coupon.setStartTime(DateUtil.parse(start));
        coupon.setEndTime(DateUtil.parse(end));
        coupon.setOverdueTime(DateUtil.parse(overDue));
        coupon.setDiscount(discount);
        coupon.setCount(count);
        coupon.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        int res = couponMapper.insert(coupon);

        return res > 0 ? "success" : "新增失败";
    }

    @Override
    public String addUserCoupon(String uid, String cid) {
        UserCoupon userCoupon
                = userCouponMapper.selectOne(
                        new QueryWrapper<UserCoupon>()
                                .eq("uid", uid).eq("cid",cid));
        if(userCoupon != null)
            return "您已拥有该优惠券";

        Coupon coupon = couponMapper.selectById(cid);
        if(coupon.getCount() == 0)
            return "优惠券余量不足";

        userCouponMapper.insert(new UserCoupon(uid, cid));
        coupon.setCount(coupon.getCount() - 1);
        couponMapper.updateById(coupon);

        return "success";
    }

    @Override
    public String deleteUserCoupon(String uid, String cid) {
        UserCoupon userCoupon
                = userCouponMapper.selectOne(
                new QueryWrapper<UserCoupon>()
                        .eq("uid", uid).eq("cid",cid));
        if(userCoupon == null)
            return "success";
        userCouponMapper.delete(new UpdateWrapper<UserCoupon>()
                .eq("uid", uid)
                .eq("cid", cid));
        Coupon coupon = couponMapper.selectById(cid);
        coupon.setCount(coupon.getCount() + 1);
        couponMapper.updateById(coupon);
        return "success";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String rechargeHotCard(String uid, String cid, Double money, String payPassword, Double discount) {
        HotCard hotCard = hotCardMapper.selectOne(
                new QueryWrapper<HotCard>()
                        .eq("uid",uid));
        BankUser user = bankUserMapper.selectById(uid);
        if(hotCard == null || user == null)
            return "没有该用户";
        if(!StringUtils.equals(MD5Util.encoderByMd5(payPassword), user.getPayPassword()))
            return "支付密码错误";

        hotCard.setMoney(hotCard.getMoney() + money);
        hotCardMapper.updateById(hotCard);

        double disMoney = money;
        if(cid != null){//如果使用了优惠券
            disMoney = money * discount;
            userCouponMapper.delete(new UpdateWrapper<UserCoupon>()
                    .eq("uid", uid)
                    .eq("cid", cid));
        }
        user.setMoney(user.getMoney() - disMoney);

        bankUserMapper.updateById(user);

        return "success";
    }

    @Override
    public String rechargeCanteenCard(String uid, String cid, Double money, String payPassword, Double discount) {
        CanteenCard canteenCard = canteenCardMapper.selectOne(
                new QueryWrapper<CanteenCard>()
                        .eq("uid",uid));
        BankUser user = bankUserMapper.selectById(uid);

        if(canteenCard == null)
            return "没有该用户";
        if(!StringUtils.equals(MD5Util.encoderByMd5(payPassword), user.getPayPassword()))
            return "支付密码错误";

        canteenCard.setMoney(canteenCard.getMoney() + money);
        canteenCardMapper.updateById(canteenCard);

        double disMoney = money;
        if(cid != null){//如果使用了优惠券
            disMoney = money * discount;
            userCouponMapper.delete(new UpdateWrapper<UserCoupon>()
                    .eq("uid", uid)
                    .eq("cid", cid));
        }
        user.setMoney(user.getMoney() - disMoney);

        bankUserMapper.updateById(user);

        return "success";
    }

    @Override
    public CardVo getUserHotCard(String uid) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user == null) return null;
        HotCard card = hotCardMapper.selectById(uid);
        return new CardVo(user.getMoney(), card.getCardId(), card.getMoney());
    }

    @Override
    public CardVo getUserCanteenCard(String uid) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user == null) return null;
        CanteenCard card = canteenCardMapper.selectById(uid);
        return new CardVo(user.getMoney(), card.getCardId(), card.getMoney());
    }

    @Override
    public String bindCard(String uid, String cid, String cardName) {
        BankUser user = bankUserMapper.selectById(uid);
        if(user == null)
            return "没有该用户";
        BankCard card = bankCardMapper.selectOne(
                new QueryWrapper<BankCard>()
                        .eq("uid",uid)
                        .eq("cid",cid));
        if(card != null)
            return "您已经绑定了该卡";
        bankCardMapper.insert(new BankCard(uid, cid,
                cardName + "|尾号: " + cid.substring(cid.length() > 3 ? cid.length() - 4 : 0)));


        return "success";
    }

    @Override
    public List<Notice> getAllNotice() {
        List<Notice> res = noticeMapper.selectList(new QueryWrapper<Notice>().last("limit 5"));
        res.forEach(item -> {
            if(item.getTitle().length() > 10){
                item.setTitle(item.getTitle().substring(0, 10) + "....");
            }
            item.setViewDate(formatter2.format(item.getCreateTime()));
        });
        return res;
    }

    @Override
    public Notice getNoticeById(String id) {
        Notice res = noticeMapper.selectById(id);
        res.setViewDate(formatter2.format(res.getCreateTime()));
        return res;
    }

    @Override
    public String addNotice(String title, String publisher, String type, String content) {
        Notice notice = new Notice();
        notice.setTitle(title);
        notice.setPublisher(publisher);
        notice.setContent(content);
        notice.setType(type);
        notice.setCreateTime(new Date());
        notice.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        int res = noticeMapper.insert(notice);
        return res > 0 ? "success" : "添加失败";
    }

    @Override
    public String updateNotice(String id, String title, String publisher, String type, String content) {
        Notice notice = noticeMapper.selectById(id);
        if(notice == null)
            return "该公告已被删除，请刷新页面";
        notice.setTitle(title);
        notice.setPublisher(publisher);
        notice.setType(type);
        notice.setContent(content);
        int res = noticeMapper.updateById(notice);

        return res > 0 ? "success" : "更新失败";
    }

    @Override
    public List<Notice> getNoticeByConditions(String title, String publisher, String type, String start, String end) {
        QueryWrapper<Notice> queryWrapper = new QueryWrapper<>();
        if(StringUtils.isNotBlank(title)) queryWrapper.eq("title", title);
        if(StringUtils.isNotBlank(publisher)) queryWrapper.eq("publisher", publisher);
        if(StringUtils.isNotBlank(type)) queryWrapper.eq("type", type);

        if (StringUtils.isNotBlank(start) && StringUtils.isNotBlank(end)){
            start = formatter2.format(new Date(start));
            end = formatter2.format(new Date(end));
            queryWrapper.lt("create_time", end);
            queryWrapper.gt("create_time", start);
        }

        return noticeMapper.selectList(queryWrapper);
    }

    @Override
    public List<Coupon> getUserAllHotCardCoupon(String uid) {
        List<UserCoupon> userCoupons = userCouponMapper.selectList(
                new QueryWrapper<UserCoupon>().eq("uid", uid));

        if(userCoupons == null || userCoupons.isEmpty())
            return null;
        List<Coupon> allCoupons = couponMapper.selectBatchIds(
                        userCoupons.stream()
                        .map(UserCoupon::getCid)
                        .collect(Collectors.toList()));
        List<Coupon> coupons = new ArrayList<>();
        allCoupons.forEach(item ->{
            if(item.getOverdueTime().after(new Date()) && item.getCouponType().equals("hot")){
                coupons.add(item);
            }
        });
        return coupons;
    }

    @Override
    public List<Coupon> getUserAllCanteenCoupon(String uid) {
        List<UserCoupon> userCoupons = userCouponMapper.selectList(
                new QueryWrapper<UserCoupon>().eq("uid", uid));

        if(userCoupons == null || userCoupons.isEmpty())
            return null;
        List<Coupon> allCoupons = couponMapper.selectBatchIds(
                userCoupons.stream()
                        .map(UserCoupon::getCid)
                        .collect(Collectors.toList()));
        List<Coupon> coupons = new ArrayList<>();
        allCoupons.forEach(item ->{
            if(item.getOverdueTime().after(new Date()) && item.getCouponType().equals("canteen")){
                coupons.add(item);
            }
        });
        return coupons;
    }


    private List<Coupon> formatterCouponList(List<Coupon> coupons){
        coupons.forEach(item -> {
            item.setViewEndTime(formatter.format(item.getEndTime()));
            item.setViewStartTime(formatter.format(item.getStartTime()));
            item.setViewOverdueTime(formatter.format(item.getOverdueTime()));
        });
        return coupons;
    }

    private <T> List<T> changeListSize(List<T> list, int mod){
        T first = list.get(0);
        T last = list.get(list.size() - 1);

        List<T> res = new ArrayList<>();
        res.add(first);
        for(int i = 1; i < list.size() - 1; i++){
            if(i % mod != 0)
                res.add(list.get(i));
        }
        res.add(last);
        return res;
    }

}
